
//NdkMediaCodec.cpp
//canok 20210318
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include <unistd.h>
#include "media/NdkMediaCodec.h"
 
#define LOGD printf
bool bRun = true;
AMediaCodec* pMediaCodec;
AMediaFormat *format ; /* 
FILE *fp_in = NULL;
FILE *fp_out = NULL;*/ 
int mW = 4000;
int mH=3000;
int fps=60;
int64_t getNowUs(){
    timeval tv;
    gettimeofday(&tv, 0);
    return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_usec;
}
 
int in_frame=0;
int out_frame=0;
int64_t lastOuttime=0;
pthread_mutex_t startMutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t startCond=PTHREAD_COND_INITIALIZER;
char buff[1024];
void *run(void*pram){
 /*
    fp_in = fopen("/storage/emulated/0/canok/1080p60.yuv","r");
    if(NULL == fp_in){
        LOGD("[%s][%d]fopen erro, no inputfile!\n",__FUNCTION__ ,__LINE__);
        exit(-1);
    }
    fp_out = fopen("/storage/emulated/0/canok/out_1080p60.h264","w+");
    if(NULL == fp_out){
        LOGD("[%s][%d]fopen erro\n",__FUNCTION__ ,__LINE__);
        exit(-1);
    }
 */ 
    //https://github.com/android/ndk-samples/blob/main/native-codec/app/src/main/cpp/native-codec-jni.cpp
    //decode
    //这里设定名称
    pMediaCodec = AMediaCodec_createEncoderByType("video/avc");//h264 // 创建 codec 编码器
    if(pMediaCodec == NULL){
        fprintf(stderr,"createEncoder erro[%s%d]\n",__FUNCTION__ ,__LINE__);
    }
    format = AMediaFormat_new();
 
    AMediaFormat_setString(format, /*"mime"*/AMEDIAFORMAT_KEY_MIME, "video/avc");
    /*
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_WIDTH,mW);
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_HEIGHT,mH);
    //在OMX_IVCommon.h https://www.androidos.net.cn/android/9.0.0_r8/xref/frameworks/native/headers/media_plugin/media/openmax/OMX_IVCommon.h
   AMediaFormat_setInt32(format,"color-format",0x7f420888);
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_COLOR_FORMAT,19);
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_FRAME_RATE,30);
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_BIT_RATE,6000000);
    AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,1);
 */
    AMediaFormat_setInt32(format,"color-format", /*2130708361*/0x7fa30c00);
    AMediaFormat_setInt32(format,"i-frame-interval", 1);
    AMediaFormat_setInt32(format,"level", 65536);
    AMediaFormat_setString(format,"mime", "video/avc");
    AMediaFormat_setInt32(format,"profile" ,8);
    AMediaFormat_setInt32(format,"width", mW);
    AMediaFormat_setInt32(format,"bitrate" ,8000000);
    AMediaFormat_setInt32(format,"frame-rate" ,60);
    AMediaFormat_setInt32(format,"height", mH);

    LOGD("[%s][%s][%d]fromat:%s\n",__FUNCTION__ ,__DATE__,__LINE__,AMediaFormat_toString(format));
 
    //这里配置 format
    media_status_t status = AMediaCodec_configure(pMediaCodec,format,NULL,NULL,AMEDIACODEC_CONFIGURE_FLAG_ENCODE);//解码，flags 给0，编码给AMEDIACODEC_CONFIGURE_FLAG_ENCODE
    if(status!=0){
        LOGD("erro config %d\n",status);
        return NULL;
    }
 
 
    AMediaCodec_start(pMediaCodec);
    if(status!=0){
        LOGD("start erro %d\n",status);
        return NULL;
    }
//be ready for getout thread
    pthread_mutex_lock(&startMutex);
    pthread_cond_signal(&startCond);
    pthread_mutex_unlock(&startMutex);
    char  u=0;
    int T=clock();
    int TT=0; 
    if(fps==0) 
	    fps=60; 
    fps=1000/fps; //1000 1秒等于1000豪秒
    fps=fps*1000; //一毫秒等于1000 us  
    while(bRun){
        //请求buffer
        //如果超时时间设置为0， 就相当于轮训访问的方式，非常占用CPU，所以这里设置为-1，无限超时
        ssize_t bufidx = AMediaCodec_dequeueInputBuffer(pMediaCodec,-1);
        //LOGD("input buffer %zd\n",bufidx);
        if(bufidx>=0) {
            size_t bufsize;
            int64_t pts = getNowUs();
            uint8_t *buf = AMediaCodec_getInputBuffer(pMediaCodec, bufidx, &bufsize);
            //填充yuv数据
            //如只是测试编码速度，这里读文件操作也应该优化，预先载入到内存中，直接从内存中拷贝，降低耗时。
            int frameLenYuv = mW * mH * 3 / 2;
	    /* 
            if (fread(buf, 1, frameLenYuv, fp_in) < frameLenYuv) {
                fseek(fp_in, 0, SEEK_SET);
                fread(buf, 1, frameLenYuv, fp_in);
            }*/ 
	    memset(buf,u,frameLenYuv); 
	    u++; 
	    if(u==255) 
		    u=0; 
            //入队列
	    /*
	    if((clock()-T)>1000000){*/ 
            //sprintf(buff,"fps[%d] pts:%ld pthread:%ld\r", in_frame, pts,pthread_self());
	    //fps=in_frame; 
	    //write(2,buff,strlen(buff));
	    TT=fps-(clock()-T); 
	    if(TT>0)usleep(TT); 
	    T=clock();
	  //  }
	    
	    in_frame++; 
            AMediaCodec_queueInputBuffer(pMediaCodec, bufidx, 0, frameLenYuv, pts, 0);
        }
    }
}

void getout(){
    AMediaCodecBufferInfo info;
    //取输出buffer
    //如果超时时间设置为0， 就相当于轮训访问的方式，非常占用CPU，所以这里设置为-1，无限超时
    //auto outindex = AMediaCodec_dequeueOutputBuffer(pMediaCodec, &info, 0);
    auto outindex = AMediaCodec_dequeueOutputBuffer(pMediaCodec, &info, -1);
    if (outindex >= 0) {
        //在这里取走编码后的数据
        //释放buffer给编码器
        size_t outsize;
        uint8_t *buf = AMediaCodec_getOutputBuffer(pMediaCodec,outindex,&outsize);
        //如果只是测试编码速度，这里写文件操作要不要做，直接释放buffer，尽量降低耗时
        //fwrite(buf,1,info.size,fp_out);
	int osize=info.size; 
	write(1,&osize,sizeof(int)); 
	write(1,buf,info.size); 
        if(1) {
            AMediaFormat *format2 = AMediaCodec_getOutputFormat(pMediaCodec);
            int32_t frameRate,w,h,color;
            AMediaFormat_getInt32(format2,AMEDIAFORMAT_KEY_FRAME_RATE,&frameRate);
            AMediaFormat_getInt32(format2,AMEDIAFORMAT_KEY_WIDTH,&w);
            AMediaFormat_getInt32(format2,AMEDIAFORMAT_KEY_HEIGHT,&h);
	    out_frame++; 
            //sprintf(buff,"out[%d] pts %lld %dX%d fps: %d %d pthread:%d\r",out_frame++,info.presentationTimeUs,w,h,frameRate,info.size,pthread_self());
            //write(2,buff,strlen(buff)); 
	    int64_t nowtime = getNowUs();
            //LOGD("frame stay times:%lld,  out_gap:%lld\n",nowtime-info.presentationTimeUs,nowtime-lastOuttime);
            lastOuttime = nowtime;
        }
        AMediaCodec_releaseOutputBuffer(pMediaCodec, outindex, false);
    }
}
void *getoutthread(void*pram){
    pthread_mutex_lock(&startMutex);
    pthread_cond_wait(&startCond,&startMutex);
    pthread_mutex_unlock(&startMutex);
    while(true){
        getout();
    }
}
void *fp(void *p){ 
	int fps=0;
	int ofps=0; 
	while(1){
		sleep(1); 
	sprintf(buff,"fps: in %d/s out %d/s  %dx%d\r", in_frame-fps,out_frame-ofps,mW,mH);
            fps=in_frame;
	    ofps=out_frame; 
            write(2,buff,strlen(buff)); 
	}
}
int main(int argc, const char*argv[]){
	if(argc==2) 
		fps=atoi(argv[1]); 
    //双线程方式，一线程放数据，一线程取数据，确保编码器随时有数据可用，且编码完的数据立即被取走，不占用buffer
    int ret =0;
    pthread_t pid;
    if((ret=pthread_create(&pid,NULL,run,NULL)) !=0 ){
        LOGD("thread_create err\n");
        return -1;
    }
    if((ret=pthread_create(&pid,NULL,getoutthread,NULL)) !=0 ){
        LOGD("thread_create err\n");
        return -1;
    }
    if((ret=pthread_create(&pid,NULL,fp,NULL)) !=0 ){                                                   
	    LOGD("thread_create err\n");
        return -1;                                                                                            }
    while(1){
        usleep(1000*1000);
    }
}

